<?php
/*
 * @Description: 微信支付设置
 * @Author: QianLong
 * @Date: 2019-09-27 16:34:02
 * @LastEditors    : QianLong
 * @LastEditTime   : 2022-05-28 17:20:57
 */

namespace app\common\service\wxpay;

use app\common\RedisCache;
use app\lib\exception\ApiException;

class Config
{
    protected $infoData;
    const KEY_LENGTH_BYTE = 32;
    const AUTH_TAG_LENGTH_BYTE = 16;
    public function __construct()
    {
        $this->infoData = (new RedisCache())::getSysPaySetting('wxpay');
        if (empty($this->infoData)) {
            throw new ApiException("微信配置为空", 1);
        }
    }
    public function getSetting()
    {
        return $this->infoData;
    }
    /**
     * Decrypt AEAD_AES_256_GCM ciphertext
     *
     * @param string    $associatedData     AES GCM additional authentication data
     * @param string    $nonceStr           AES GCM nonce
     * @param string    $ciphertext         AES GCM cipher text
     *
     * @return string|bool      Decrypted string on success or FALSE on failure
     */
    public function decryptToString($associatedData, $nonceStr, $ciphertext)
    {
        $ciphertext = \base64_decode($ciphertext);
        if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) {
            return false;
        }
        // ext-sodium (default installed on >= PHP 7.2)
        if (
            function_exists('\sodium_crypto_aead_aes256gcm_is_available') &&
            \sodium_crypto_aead_aes256gcm_is_available()
        ) {
            return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->infoData['api_v3_code']);
        }
        // ext-libsodium (need install libsodium-php 1.x via pecl)
        if (
            function_exists('\Sodium\crypto_aead_aes256gcm_is_available') &&
            \Sodium\crypto_aead_aes256gcm_is_available()
        ) {
            return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->infoData['api_v3_code']);
        }
        // openssl (PHP >= 7.1 support AEAD)
        if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
            $ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);
            $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);
            return \openssl_decrypt(
                $ctext,
                'aes-256-gcm',
                $this->infoData['api_v3_code'],
                \OPENSSL_RAW_DATA,
                $nonceStr,
                $authTag,
                $associatedData
            );
        }
        throw new ApiException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
    }
    /**
     * 获取Authorization
     * @param string $requestUrl
     * @param array $reqParams
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2021-04-06 15:45:20
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-04-06 15:45:20
     */
    public function getAuthorization(string $requestUrl, array $reqParams = null, string $method = 'POST')
    {
        $body = $reqParams ?  json_encode($reqParams) : '';
        $nonce = createId();
        $timestamp = time();
        $message = $this->buildMessage($nonce, $timestamp, $requestUrl, $body, $method);
        $sign = $this->generalSign($message);
        $serialNo = PemUtil::parseCertificateSerialNo(file_get_contents(root_path() . '/crt/' . $this->infoData['merchant_certificate']));
        return 'WECHATPAY2-SHA256-RSA2048 ' . sprintf(
            'mchid="%s",nonce_str="%s",timestamp="%d",serial_no="%s",signature="%s"',
            $this->infoData['mch_id'],
            $nonce,
            $timestamp,
            $serialNo,
            $sign
        );
    }
    /**
     * 构建签名参数
     * @param [type] $nonce
     * @param [type] $timestamp
     * @param [type] $requestUrl
     * @param string $body
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2021-04-06 15:45:03
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-04-06 15:45:03
     */
    private function buildMessage($nonce, $timestamp,string $requestUrl, $body = '', $method)
    {
        $urlParts = parse_url($requestUrl);
        $canonicalUrl = ($urlParts['path'] . (!empty($urlParts['query']) ? "?{$urlParts['query']}" : ""));
        return strtoupper($method) . "\n" .
            $canonicalUrl . "\n" .
            $timestamp . "\n" .
            $nonce . "\n" .
            $body . "\n";
    }
    /**
     * 生成签名
     * @param [type] $data
     * @return void
     * @author QianLong <87498106@qq.com>
     * @date 2021-04-05 15:05:44
     * @editAuthor QianLong <87498106@qq.com>
     * @editDescription 
     * @editDate 2021-04-05 15:05:44
     */
    protected function generalSign($signStr)
    {
        $priKey = file_get_contents(root_path() . '/crt/' . $this->infoData['merchant_private_key']);
        $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap($priKey, 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----";
        ($res) or die('您使用的私钥格式错误，请检查RSA私钥配置');

        openssl_sign($signStr, $sign, $priKey, 'sha256WithRSAEncryption');
        $sign = base64_encode($sign);
        return $sign;
    }
}
